﻿Imports System
Imports System.Collections
Imports System.Collections.Generic
Imports System.Globalization
Imports System.Text
Imports System.Web

'Namespace FacebookLogin
Public Class JSON
    ' <summary>
    ' This class encodes and decodes JSON strings. Based on Patrick van Bergen's code.
    ' Modified by Jitbit Sofwtare (Alex Yumashev)
    ' The software is subject to the MIT license: you are free to use it in any way you like, but it must keep its license.
    '</summary>

    Public Shared TOKEN_NONE As Integer = 0
    Public Shared TOKEN_CURLY_OPEN As Integer = 1
    Public Shared TOKEN_CURLY_CLOSE As Integer = 2
    Public Shared TOKEN_SQUARED_OPEN As Integer = 3
    Public Shared TOKEN_SQUARED_CLOSE As Integer = 4
    Public Shared TOKEN_COLON As Integer = 5
    Public Shared TOKEN_COMMA As Integer = 6
    Public Shared TOKEN_STRING As Integer = 7
    Public Shared TOKEN_NUMBER As Integer = 8
    Public Shared TOKEN_TRUE As Integer = 9
    Public Shared TOKEN_FALSE As Integer = 10
    Public Shared TOKEN_NULL As Integer = 11

    Private Shared BUILDER_CAPACITY As Integer = 2000

    ' <summary>
    ' Parses the string json into a value
    ' </summary>
    ' <param name="json">A JSON string.</param>
    ' <returns>An ArrayList, a Hashtable, a double, a string, null, true, or false</returns>
    Public Shared Function JsonDecode(ByVal json As String) As Object
        Dim success As Boolean = True

        Return JsonDecode(json, success)
    End Function
    ' <summary>
    ' Parses the string json into a value; and fills 'success' with the successfullness of the parse.
    ' </summary>
    ' <param name="json">A JSON string.</param>
    ' <param name="success">Successful parse?</param>
    ' <returns>An ArrayList, a Hashtable, a double, a string, null, true, or false</returns>
    Public Shared Function JsonDecode(ByVal json As String, ByRef success As Boolean) As Object
        success = True
        If json IsNot Nothing Then
            Dim charArray() As Char = json.ToCharArray()
            Dim index As Integer = 0
            Dim value As Object = ParseValue(charArray, index, success)
            Return value
        Else
            Return Nothing
        End If
    End Function

    Protected Shared Function ParseObject(ByVal json() As Char, ByRef index As Integer, ByRef success As Boolean) As Hashtable
        Dim table As New Hashtable()
        Dim token As Integer
        NextToken(json, index)

        Do
            token = LookAhead(json, index)
            If token = TOKEN_NONE Then
                success = False
                Return Nothing
            ElseIf token = TOKEN_COMMA Then
                NextToken(json, index)
            ElseIf token = TOKEN_CURLY_CLOSE Then
                NextToken(json, index)
                Return table
            Else

                ' name
                Dim name As String = ParseString(json, index, success)
                If (Not success) Then
                    success = False
                    Return Nothing
                End If

                ' :
                token = NextToken(json, index)
                If token <> TOKEN_COLON Then
                    success = False
                    Return Nothing
                End If

                ' value
                Dim value As Object = ParseValue(json, index, success)
                If (Not success) Then
                    success = False
                    Return Nothing
                End If

                table(name) = value
            End If
        Loop

        Return table
    End Function

    Protected Shared Function ParseArray(ByVal json() As Char, ByRef index As Integer, ByRef success As Boolean) As ArrayList
        Dim array As New ArrayList()

        ' [
        NextToken(json, index)

        Do
            Dim token As Integer = LookAhead(json, index)
            If token = TOKEN_NONE Then
                success = False
                Return Nothing
            ElseIf token = TOKEN_COMMA Then
                NextToken(json, index)
            ElseIf token = TOKEN_SQUARED_CLOSE Then
                NextToken(json, index)
                Exit Do
            Else
                Dim value As Object = ParseValue(json, index, success)
                If (Not success) Then
                    Return Nothing
                End If

                array.Add(value)
            End If
        Loop

        Return array
    End Function

    Protected Shared Function ParseValue(ByVal json() As Char, ByRef index As Integer, ByRef success As Boolean) As Object
        Select Case LookAhead(json, index)
            Case TOKEN_STRING
                Return ParseString(json, index, success)
            Case TOKEN_NUMBER
                Return ParseNumber(json, index)
            Case TOKEN_CURLY_OPEN
                Return ParseObject(json, index, success)
            Case TOKEN_SQUARED_OPEN
                Return ParseArray(json, index, success)
            Case TOKEN_TRUE
                NextToken(json, index)
                Return Boolean.Parse("TRUE")
            Case TOKEN_FALSE
                NextToken(json, index)
                Return Boolean.Parse("FALSE")
            Case TOKEN_NULL
                NextToken(json, index)
                Return Nothing
            Case TOKEN_NONE
        End Select

        success = False
        Return Nothing
    End Function

    Protected Shared Function ParseString(ByVal json() As Char, ByRef index As Integer, ByRef success As Boolean) As String
        Dim s As New StringBuilder(BUILDER_CAPACITY)
        Dim c As Char

        EatWhitespace(json, index)

        ' "
        c = json(index)
        index += 1

        Dim complete As Boolean = False
        Do While Not complete

            If index = json.Length Then
                Exit Do
            End If

            c = json(index)
            index += 1
            If c = """" Then
                complete = True
                Exit Do
            ElseIf c = "\" Then

                If index = json.Length Then
                    Exit Do
                End If
                c = json(index)
                index += 1
                If c = """" Then
                    s.Append("""")
                ElseIf c = "\" Then
                    s.Append("\")
                ElseIf c = "/" Then
                    s.Append("/")
                ElseIf c = "b" Then
                    s.Append(ControlChars.Back)
                ElseIf c = "f" Then
                    s.Append(ControlChars.FormFeed)
                ElseIf c = "n" Then
                    s.Append(ControlChars.Lf)
                ElseIf c = "r" Then
                    s.Append(ControlChars.Cr)
                ElseIf c = "t" Then
                    s.Append(ControlChars.Tab)
                ElseIf c = "u" Then
                    Dim remainingLength As Integer = json.Length - index
                    If remainingLength >= 4 Then
                        ' fetch the next 4 chars
                        Dim unicodeCharArray(3) As Char
                        Array.Copy(json, index, unicodeCharArray, 0, 4)
                        ' parse the 32 bit hex into an integer codepoint
                        Dim codePoint As UInteger = UInt32.Parse(New String(unicodeCharArray), NumberStyles.HexNumber)
                        ' convert the integer codepoint to a unicode char and add to string
                        s.Append(Char.ConvertFromUtf32(CInt(Fix(codePoint))))
                        ' skip 4 chars
                        index += 4
                    Else
                        Exit Do
                    End If
                End If

            Else
                s.Append(c)
            End If

        Loop

        If (Not complete) Then
            success = False
            Return Nothing
        End If

        Return s.ToString()
    End Function

    Protected Shared Function ParseNumber(ByVal json() As Char, ByRef index As Integer) As Double
        EatWhitespace(json, index)

        Dim lastIndex As Integer = GetLastIndexOfNumber(json, index)
        Dim charLength As Integer = (lastIndex - index) + 1
        Dim numberCharArray(charLength - 1) As Char

        Array.Copy(json, index, numberCharArray, 0, charLength)
        index = lastIndex + 1
        Return Double.Parse(New String(numberCharArray), CultureInfo.InvariantCulture)
    End Function

    Protected Shared Function GetLastIndexOfNumber(ByVal json() As Char, ByVal index As Integer) As Integer
        Dim lastIndex As Integer

        For lastIndex = index To json.Length - 1
            If "0123456789+-.eE".IndexOf(json(lastIndex)) = -1 Then
                Exit For
            End If
        Next lastIndex
        Return lastIndex - 1
    End Function

    Protected Shared Sub EatWhitespace(ByVal json() As Char, ByRef index As Integer)
        Do While index < json.Length
            If " \t\n\r".IndexOf(json(index)) = -1 Then
                Exit Do
            End If
            index += 1
        Loop
    End Sub

    Protected Shared Function LookAhead(ByVal json() As Char, ByVal index As Integer) As Integer
        Dim saveIndex As Integer = index
        Return NextToken(json, saveIndex)
    End Function

    Protected Shared Function NextToken(ByVal json() As Char, ByRef index As Integer) As Integer
        EatWhitespace(json, index)

        If index = json.Length Then
            Return TOKEN_NONE
        End If

        Dim c As Char = json(index)
        index += 1
        Select Case c
            Case "{"
                Return TOKEN_CURLY_OPEN
            Case "}"
                Return TOKEN_CURLY_CLOSE
            Case "["
                Return TOKEN_SQUARED_OPEN
            Case "]"
                Return TOKEN_SQUARED_CLOSE
            Case ","
                Return TOKEN_COMMA
            Case """"
                Return TOKEN_STRING
            Case "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "-"
                Return TOKEN_NUMBER
            Case ":"
                Return TOKEN_COLON
        End Select
        index -= 1

        Dim remainingLength As Integer = json.Length - index

        ' false
        If remainingLength >= 5 Then
            If json(index) = "f" AndAlso json(index + 1) = "a" AndAlso json(index + 2) = "l" AndAlso json(index + 3) = "s" AndAlso json(index + 4) = "e" Then
                index += 5
                Return TOKEN_FALSE
            End If
        End If

        ' true
        If remainingLength >= 4 Then
            If json(index) = "t" AndAlso json(index + 1) = "r" AndAlso json(index + 2) = "u" AndAlso json(index + 3) = "e" Then
                index += 4
                Return TOKEN_TRUE
            End If
        End If

        ' null
        If remainingLength >= 4 Then
            If json(index) = "n" AndAlso json(index + 1) = "u" AndAlso json(index + 2) = "l" AndAlso json(index + 3) = "l" Then
                index += 4
                Return TOKEN_NULL
            End If
        End If

        Return TOKEN_NONE
    End Function
End Class
'End Namespace